package com.zxy.study.base.recyclerview;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.LinearLayout;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import androidx.annotation.CallSuper;
import androidx.annotation.IdRes;
import androidx.annotation.IntDef;
import androidx.annotation.IntRange;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;


/**
 * @author : Shonn.zhang
 * @email : shonn.zhang@mart.blue
 * @date : 2020/7/20 17:08
 * @desc :
 * 1、封装通用的数据操作
 * 2、可设置 HeaderView 和 FooterView
 * 3、可设置ItemView的点击事件及ItemView的ChildView点击事件
 */
public abstract class BaseAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private static final int TYPE_HEADER_VIEW = Integer.MIN_VALUE;
    private static final int TYPE_FOOTER_VIEW = TYPE_HEADER_VIEW + 1;

    protected Context mContext;
    protected RecyclerView mParent;
    protected LayoutInflater mInflater;

    //HeaderView容器
    private LinearLayout mHeaderLayout;

    //FooterView容器
    private LinearLayout mFooterLayout;

    //占满整个屏幕的HeaderView
    private View mMatchParentHeaderView;

    //点击事件
    private OnItemClickListener mItemClickListener;
    private OnItemLongClickListener mItemLongClickListener;
    private OnItemClickListener mItemChildClickListener;
    private OnItemLongClickListener mItemChildLongClickListener;

    /**
     * 存储ItemView中子View的id,用于设置子View的点击事件
     */
    private List<Integer> mChildViewClickIds = new ArrayList<>();

    /**
     * 存储ItemView中子View的id,用于设置子View的长按事件
     */
    private List<Integer> mChildViewLongClickIds = new ArrayList<>();

    /**
     * 数据容器不为null
     * PS: 最好通过给出的方法访问，不要直接操作
     */
    @NonNull
    private final List<T> mDataList = new ArrayList<>();

    @IntDef({LinearLayout.VERTICAL, LinearLayout.HORIZONTAL})
    @Retention(RetentionPolicy.SOURCE)
    public @interface Orientation {
    }

    public BaseAdapter() {
    }

    public BaseAdapter(List<T> dataList) {
        if (dataList != null && !dataList.isEmpty()) {
            mDataList.addAll(dataList);
        }
    }

    /**
     * 为了不破坏BaseAdapter原有结构，扩展请重写 {@link #onDefaultCreateViewHolder(ViewGroup, int)}
     */
    @NonNull
    @Override
    public final RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == TYPE_HEADER_VIEW && mHeaderLayout != null) {
            ViewParent headerParent = mHeaderLayout.getParent();
            if (headerParent != null && headerParent instanceof ViewGroup) {
                ((ViewGroup) headerParent).removeView(mHeaderLayout);
            }
            return new BaseViewHolder(mHeaderLayout);

        } else if (viewType == TYPE_FOOTER_VIEW && mFooterLayout != null) {
            ViewParent footerParent = mFooterLayout.getParent();
            if (footerParent != null && footerParent instanceof ViewGroup) {
                ((ViewGroup) footerParent).removeView(mHeaderLayout);
            }
            return new BaseViewHolder(mFooterLayout);

        } else {
            VH vh = onDefaultCreateViewHolder(parent, viewType);
            bindClickListener(vh);
            return vh;
        }
    }

    public abstract VH onDefaultCreateViewHolder(@NonNull ViewGroup parent, int viewType);

    /**
     * 为了不破坏BaseAdapter原有结构，扩展请重写 {@link #onDefaultBindViewHolder(RecyclerView.ViewHolder, Object, int)}
     */
    @Override
    public final void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (hasHeaderView()) {
            position -= getHeaderViewCount();
        }

        if (position >= 0 && position < getDataSize()) {
            VH vh = (VH) holder;
            onDefaultBindViewHolder(vh, getItemData(position), position);
        }
    }

    public abstract void onDefaultBindViewHolder(VH holder, T item, int position);

    /**
     * 为了不破坏BaseAdapter原有结构，扩展请重写 {@link #getDefaultItemViewType(int)}
     */
    @Override
    public final int getItemViewType(int position) {
        if (hasHeaderView() && position >= 0 && position < getHeaderViewCount()) {
            return TYPE_HEADER_VIEW;
        } else {
            position -= getHeaderViewCount();
            if (position >= 0 && position < mDataList.size()) {
                return getDefaultItemViewType(position);
            } else {
                return TYPE_FOOTER_VIEW;
            }
        }
    }

    /**
     * 如果要自定ItemView的type类型，重写该方法，不要重写{@link #getItemViewType(int)}
     * 否则添加HeaderView和FooterView就不会生效
     *
     * @param position
     * @return
     */
    public int getDefaultItemViewType(int position) {
        return super.getItemViewType(position);
    }

    /**
     * 为了不破坏BaseAdapter原有结构，扩展请重写 {@link #getDefaultItemCount()}
     */
    @Override
    public final int getItemCount() {
        return getDefaultItemCount() + getHeaderViewCount() + getFooterViewCount();
    }

    public int getDefaultItemCount() {
        return mDataList.size();
    }

    public int getDataSize() {
        return mDataList.size();
    }

    /**
     * 绑定ItemView及其子View的点击事件
     *
     * @param vh
     */
    private void bindClickListener(final VH vh) {
        if (vh == null) return;

        final View itemView = vh.itemView;

        //ItemView点击事件
        if (mItemClickListener != null) {
            itemView.setOnClickListener(view -> {
                int position = vh.getAdapterPosition();
                if (position == RecyclerView.NO_POSITION) {
                    return;
                }
                position -= getHeaderViewCount();
                mItemClickListener.onItemClick(BaseAdapter.this, itemView, position);
            });
        }

        //ItemView长按事件
        if (mItemLongClickListener != null) {
            itemView.setOnLongClickListener(view -> {
                int position = vh.getAdapterPosition();
                if (position != RecyclerView.NO_POSITION) {
                    position -= getHeaderViewCount();
                    return mItemLongClickListener.onItemLongClick(BaseAdapter.this, itemView,
                            position);
                }
                return false;
            });
        }

        //ItemView的ChildView点击事件
        if (mItemChildClickListener != null) {
            for (int i = 0; i < mChildViewClickIds.size(); i++) {
                View childView = itemView.findViewById(mChildViewClickIds.get(i));
                if (childView == null) return;

                if (!childView.isClickable()) {
                    childView.setClickable(true);
                }
                childView.setOnClickListener(view -> {
                    int position = vh.getAdapterPosition();
                    if (position == RecyclerView.NO_POSITION) {
                        return;
                    }
                    position -= getHeaderViewCount();
                    mItemChildClickListener.onItemClick(BaseAdapter.this, view, position);
                });
            }
        }

        //ItemView的ChildView长按事件
        if (mItemChildLongClickListener != null) {
            for (int i = 0; i < mChildViewLongClickIds.size(); i++) {
                View childView = itemView.findViewById(mChildViewLongClickIds.get(i));
                if (childView == null) return;

                if (!childView.isClickable()) {
                    childView.setClickable(true);
                }
                childView.setOnLongClickListener(view -> {
                    int position = vh.getAdapterPosition();
                    if (position != RecyclerView.NO_POSITION) {
                        position -= getHeaderViewCount();
                        return mItemChildLongClickListener.onItemLongClick(BaseAdapter.this, view, position);
                    }
                    return false;
                });
            }
        }
    }

    public void setItemClickListener(OnItemClickListener mItemClickListener) {
        this.mItemClickListener = mItemClickListener;
    }

    public void setItemLongClickListener(OnItemLongClickListener mItemLongClickListener) {
        this.mItemLongClickListener = mItemLongClickListener;
    }

    /**
     * 设置ChildView的点击事件，ChildView的id要添加在 {@link #mChildViewClickIds}
     *
     * @param mItemChildClickListener
     */
    public void setItemChildClickListener(OnItemClickListener mItemChildClickListener) {
        this.mItemChildClickListener = mItemChildClickListener;
    }

    /**
     * 设置ChildView的长按事件，ChildView的id要添加在 {@link #mChildViewLongClickIds}
     *
     * @param mItemChildLongClickListener
     */
    public void setItemChildLongClickListener(OnItemLongClickListener mItemChildLongClickListener) {
        this.mItemChildLongClickListener = mItemChildLongClickListener;
    }

    public void addClickChildViewIds(@IdRes Integer... ids) {
        if (ids == null) return;
        mChildViewClickIds.addAll(Arrays.asList(ids));
    }

    public void addLongClickChildViewIds(@IdRes Integer... ids) {
        if (ids == null) return;
        mChildViewLongClickIds.addAll(Arrays.asList(ids));
    }

    public T getItemData(int pos) {
        if (pos >= 0 && pos < getDataSize()) {
            return mDataList.get(pos);
        }
        return null;
    }

    public List<T> getDataList() {
        return mDataList;
    }

    /**
     * 会清空原来的数据，添加新数据
     *
     * @param dataList 为空，表示清空数据
     */
    public void setDataList(List<T> dataList) {
        mDataList.clear();

        if (dataList != null && !dataList.isEmpty()) {
            mDataList.addAll(dataList);
        }
        notifyDataSetChanged();
    }

    public void setData(T t, @IntRange(from = 0) int index) {
        if (t != null && index >= 0 && index < mDataList.size()) {
            mDataList.set(index, t);
            notifyItemChanged(index + getHeaderViewCount());
        }
    }

    public void addData(T data) {
        if (data == null) {
            return;
        }
        mDataList.add(data);
        notifyItemInserted(mDataList.size() + getHeaderViewCount());
    }

    public void addData(@IntRange(from = 0) int index, T data) {
        if (data == null) {
            return;
        }
        mDataList.add(index, data);
        notifyItemInserted(index + getHeaderViewCount());
    }

    public void addData(Collection<T> datas) {
        if (datas == null || datas.isEmpty()) {
            return;
        }
        mDataList.addAll(datas);
        notifyItemRangeInserted(mDataList.size() - datas.size() + getHeaderViewCount(), datas.size());
    }

    public void addData(@IntRange(from = 0) int index, Collection<T> datas) {
        if (datas == null || datas.isEmpty()) {
            return;
        }
        mDataList.addAll(index, datas);
        notifyItemRangeInserted(index + getHeaderViewCount(), datas.size());
    }

    public void removeAt(@IntRange(from = 0) int index) {
        if (index < 0 || index >= mDataList.size()) return;
        mDataList.remove(index);
        notifyItemRemoved(index + getHeaderViewCount());
    }

    public void removeData(@NonNull T data) {
        int index = mDataList.indexOf(data);
        if (index < 0) return;

        removeAt(index);
    }

    /**
     * 将Item的数据在{@link #mDataList}中的位置,转化成在Item在Adapter中的位置
     *
     * @param dataPos item的数据在{@link #mDataList}中的位置
     * @return item在 Adapter中的位置
     */
    public int getItemAdapterPosByDataPos(int dataPos) {
        return dataPos + getHeaderViewCount();
    }

    /**
     * 将Item在Adapter中的位置转化成，item数据（在{@link #mDataList}中的位置）
     *
     * @param adapterPos item在 Adapter中的位置
     * @return item的数据在{@link #mDataList}中的位置
     */
    public int getItemDataPosByAdapterPos(int adapterPos) {
        return adapterPos - getHeaderViewCount();
    }

    /**
     * 是否有添加HeaderView
     *
     * @return
     */
    public boolean hasHeaderView() {
        return mHeaderLayout != null && mHeaderLayout.getChildCount() > 0;
    }

    /**
     * 是否有添加FooterView
     *
     * @return
     */
    public boolean hasFooterView() {
        return mFooterLayout != null && mFooterLayout.getChildCount() > 0;
    }

    /**
     * 获取 HeaderView 在整个列表中的起始位置
     *
     * @return
     */
    public int getHeaderViewAdapterPos() {
        return 0;
    }

    /**
     * 获取 FooterView 在整个列表中的起始位置
     *
     * @return
     */
    public int getFooterViewPos() {
        return (hasHeaderView() ? 1 : 0) + mDataList.size();
    }

    /**
     * 在Adapter中HeaderView的个数
     *
     * @return
     */
    public int getHeaderViewCount() {
        return hasHeaderView() ? 1 : 0;
    }

    /**
     * 获取FootView的个数
     *
     * @return
     */
    public int getFooterViewCount() {
        return hasFooterView() ? 1 : 0;
    }

    public void addHeaderView(@NonNull View view) {
        int index = mHeaderLayout == null ? 0 : mHeaderLayout.getChildCount();
        addHeaderView(view, index);
    }

    public void addHeaderView(@NonNull View view, int index) {
        addHeaderView(view, index, LinearLayout.VERTICAL);
    }

    /**
     * 添加HeaderView
     *
     * @param view        要添加的HeaderView
     * @param index       HeaderView在 {@link #mHeaderLayout}中的位置
     * @param orientation {@link #mHeaderLayout} 的方向 {@link LinearLayout#HORIZONTAL, LinearLayout#VERTICAL}
     */
    public void addHeaderView(@NonNull View view, @IntRange(from = 0) int index, @Orientation int orientation) {
        if (mHeaderLayout == null || (mHeaderLayout.getChildCount() == 0 && mHeaderLayout.getOrientation() != orientation)) {
            mHeaderLayout = new LinearLayout(view.getContext());
            mHeaderLayout.setOrientation(orientation);
            RecyclerView.LayoutParams params;
            if (orientation == LinearLayout.HORIZONTAL) {
                params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            } else {
                params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            }
            mHeaderLayout.setLayoutParams(params);
        }

        int headViewCount = mHeaderLayout.getChildCount();
        if (index < 0 || index > headViewCount) {
            index = headViewCount;
        }

        if (mMatchParentHeaderView != null && index > 0 && index == headViewCount) {
            index--;
        }

        mHeaderLayout.addView(view, index);
        if (mHeaderLayout.getChildCount() == 1) {
            //刚添加HeaderView需要刷新
            notifyItemInserted(getHeaderViewAdapterPos());
        }
    }

    public void removeHeadView(View view) {
        if (!hasHeaderView()) return;

        mHeaderLayout.removeView(view);
        if (mHeaderLayout.getChildCount() == 0) {
            notifyItemRemoved(getHeaderViewAdapterPos());
        }
    }

    public void removeAllHeaderView() {
        if (!hasHeaderView()) return;

        if (mMatchParentHeaderView == null) {
            mHeaderLayout.removeAllViews();
            notifyItemRemoved(getHeaderViewAdapterPos());
        } else if (mHeaderLayout.getChildCount() > 1) {
            mHeaderLayout.removeViews(0, mHeaderLayout.getChildCount() - 1);
        }
    }

    public void addFootView(@NonNull View view) {
        int index = mFooterLayout == null ? 0 : getFooterViewCount();
        addFootView(view, index);
    }

    public void addFootView(@NonNull View view, int index) {
        addFooterView(view, index, LinearLayout.VERTICAL);
    }

    /**
     * 添加FooterView
     *
     * @param view        要添加的FooterView
     * @param index       HeaderView在 {@link #mFooterLayout}中的位置
     * @param orientation {@link #mFooterLayout} 的方向 {@link LinearLayout#HORIZONTAL, LinearLayout#VERTICAL}
     */
    public void addFooterView(@NonNull View view, @IntRange(from = 0) int index, @Orientation int orientation) {
        if (mFooterLayout == null || (mFooterLayout.getChildCount() == 0 && mFooterLayout.getOrientation() != orientation)) {
            mFooterLayout = new LinearLayout(view.getContext());
            mFooterLayout.setOrientation(orientation);
            RecyclerView.LayoutParams params;
            if (orientation == LinearLayout.HORIZONTAL) {
                params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT);
            } else {
                params = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            }
            mFooterLayout.setLayoutParams(params);
        }

        int footerViewCount = mFooterLayout.getChildCount();
        if (index < 0 || index > footerViewCount) {
            index = footerViewCount;
        }
        mFooterLayout.addView(view, index);
        if (mFooterLayout.getChildCount() == 1) {
            notifyItemInserted(getFooterViewPos());
        }
    }

    public void removeFooterView(View view) {
        if (!hasFooterView()) return;

        mFooterLayout.removeView(view);
        if (mFooterLayout.getChildCount() == 0) {
            notifyItemRemoved(getFooterViewPos());
        }
    }

    public void removeAllFooterView() {
        if (!hasFooterView()) return;

        mFooterLayout.removeAllViews();
        notifyItemRemoved(getFooterViewPos());
    }

    /**
     * see {@link #setMatchParentHeaderView(View, int, boolean)}
     */
    public void setMatchParentHeaderView(@NonNull View view, boolean clearData) {
        setMatchParentHeaderView(view, LinearLayout.VERTICAL, clearData);
    }

    /**
     * 添加一个HeaderView,占满屏幕剩下的空间
     * PS: 只能添加一个，新添加的View会覆盖之前添加的View
     *
     * @param view        HeaderView
     * @param orientation see {@link Orientation}
     * @param clearData   true: 清除 {@link #mDataList} 中的数据
     */
    public void setMatchParentHeaderView(@NonNull View view, @Orientation int orientation, boolean clearData) {
        if (clearData) {
            setDataList(Collections.EMPTY_LIST);
        }

        if (mHeaderLayout == null || mHeaderLayout.getOrientation() != orientation) {
            mHeaderLayout = new LinearLayout(view.getContext());
            mHeaderLayout.setOrientation(orientation);
            mHeaderLayout.setLayoutParams(new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        } else {
            mHeaderLayout.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
            mHeaderLayout.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
        }

        if (mMatchParentHeaderView != null) {
            mHeaderLayout.removeView(mMatchParentHeaderView);
            mMatchParentHeaderView = null;
        }

        if (view.getLayoutParams() == null) {
            view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        }

        mHeaderLayout.addView(view);
        mMatchParentHeaderView = view;

        if (mHeaderLayout.getChildCount() == 1) {
            notifyItemInserted(getHeaderViewAdapterPos());
        }
    }

    public void removeMatchParentHeaderView() {
        if (mHeaderLayout != null) {
            if (mHeaderLayout.getOrientation() == LinearLayout.VERTICAL) {
                mHeaderLayout.getLayoutParams().width = ViewGroup.LayoutParams.MATCH_PARENT;
                mHeaderLayout.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
            } else {
                mHeaderLayout.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
                mHeaderLayout.getLayoutParams().height = ViewGroup.LayoutParams.MATCH_PARENT;
            }
            if (mMatchParentHeaderView != null) {
                removeHeadView(mMatchParentHeaderView);
                mMatchParentHeaderView = null;
            }
        }
    }

    /*
     * notifyItemXXX（int position） 系列方法的参数 position 指的是 item 在Adapter中的位置，但是使用者
     * 经常会把 数据bean在 mDataList 中的位置传入position. 正常情况不会有问题，但是如果添加了HeaderView
     * 那位置可能会出现偏差，需要通过 position + getHeaderViewCount() 进行修正. 所以重新写了这部分方法，
     * 防止用错出现问题
     */
    public void notifyItemDataChanged(int position) {
        notifyItemChanged(getItemAdapterPosByDataPos(position));
    }

    public void notifyItemDataChanged(int position, @Nullable Object payload) {
        notifyItemChanged(getItemAdapterPosByDataPos(position), payload);
    }

    public void notifyItemDataRangeChanged(int positionStart, int itemCount) {
        notifyItemRangeChanged(getItemAdapterPosByDataPos(positionStart), itemCount);
    }

    public void notifyItemDataRangeChanged(int positionStart, int itemCount, @Nullable Object payload) {
        notifyItemRangeChanged(getItemAdapterPosByDataPos(positionStart), itemCount, payload);
    }

    public void notifyItemDataInserted(int position) {
        notifyItemInserted(getItemAdapterPosByDataPos(position));
    }

    public void notifyItemDataRangeInserted(int positionStart, int itemCount) {
        notifyItemRangeInserted(getItemAdapterPosByDataPos(positionStart), itemCount);
    }

    public final void notifyItemDataMoved(int fromPosition, int toPosition) {
        notifyItemMoved(getItemAdapterPosByDataPos(fromPosition), getItemAdapterPosByDataPos(toPosition));
    }

    public void notifyItemDataRemoved(int position) {
        notifyItemRemoved(getItemAdapterPosByDataPos(position));
    }

    public void notifyItemDataRangeRemoved(int positionStart, int itemCount) {
        notifyItemRangeRemoved(getItemAdapterPosByDataPos(positionStart), itemCount);
    }

    @CallSuper
    @Override
    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
        this.mParent = recyclerView;
        this.mContext = recyclerView.getContext();
        this.mInflater = LayoutInflater.from(mContext);
    }

    @CallSuper
    @Override
    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
        super.onDetachedFromRecyclerView(recyclerView);
        this.mParent = null;
        this.mContext = null;
        this.mInflater = null;
    }

    public View inflate(@LayoutRes int layoutRes, ViewGroup parent) {
        if (mInflater != null) {
            return mInflater.inflate(layoutRes, parent, false);
        } else if (parent != null) {
            return LayoutInflater.from(parent.getContext()).inflate(layoutRes, parent, false);
        }
        return null;
    }

}
